/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "SequenceComplete_fp.h"

#if CC_SequenceComplete  // Conditional expansion of this file

/*(See part 3 specification)
// Complete a sequence and flush the object.
*/
//  Return Type: TPM_RC
//      TPM_RC_MODE             'sequenceHandle' does not reference a hash or HMAC
//                              sequence object
/*
This command adds the last part of data, if any, to a hash/HMAC sequence and returns the result.
NOTE 1 This command is not used to complete an Event Sequence. TPM2_EventSequenceComplete() is
used for that purpose.

For a hash sequence, if the results of the hash will be used in a signing operation that uses a restricted
signing key, then the ticket returned by this command can indicate that the hash is safe to sign.
对于散列序列，如果该散列的结果将用于使用受限类型的签名操作
签名密钥，则该命令返回的票据可以表明散列是安全的。

If the digest is not safe to sign, then validation will be a TPMT_TK_HASHCHECK with the hierarchy set to
TPM_RH_NULL and digest set to the Empty Buffer

If hierarchy is TPM_RH_NULL, then digest in the ticket will be the Empty Buffer.

NOTE 2 Regardless of the contents of the first octets of the hashed message, if the first buffer sent to the
TPM had fewer than sizeof(TPM_GENERATED) octets, then the TPM will operate as if digest is not
safe to sign.
不管散列消息的第一个八位字节的内容如何，如果发送到 TPM 的第一个缓冲区少于 sizeof(TPM_GENERATED) 个八位字节，
那么 TPM 将像摘要不能安全签名一样运行。

NOTE 3 The ticket is only required for a signing operation that uses a restricted signing key. It is always
returned, but can be ignored if not needed
只有使用受限签名密钥的签名操作才需要该票证。 它总是返回，但如果不需要可以忽略

*/
TPM_RC
TPM2_SequenceComplete(
    SequenceComplete_In     *in,            // IN: input parameter list
    SequenceComplete_Out    *out            // OUT: output parameter list
    )
{
    HASH_OBJECT                      *hashObject;
// Input validation
    // Get hash object pointer
    hashObject = (HASH_OBJECT *)HandleToObject(in->sequenceHandle);

    // input handle must be a hash or HMAC sequence object.
    if(hashObject->attributes.hashSeq == CLEAR
       && hashObject->attributes.hmacSeq == CLEAR)
        return TPM_RCS_MODE + RC_SequenceComplete_sequenceHandle;
// Command Output
    if(hashObject->attributes.hashSeq == SET)           // sequence object for hash
    {
       // Get the hash algorithm before the algorithm is lost in CryptHashEnd
        TPM_ALG_ID       hashAlg = hashObject->state.hashState[0].hashAlg;

        // Update last piece of the data
        CryptDigestUpdate2B(&hashObject->state.hashState[0], &in->buffer.b);

        // Complete hash
        out->result.t.size = CryptHashEnd(&hashObject->state.hashState[0], 
                                         sizeof(out->result.t.buffer),
                                         out->result.t.buffer);
        // Check if the first block of the sequence has been received
        if(hashObject->attributes.firstBlock == CLEAR)
        {
            // If not, then this is the first block so see if it is 'safe'
            // to sign.
            if(TicketIsSafe(&in->buffer.b))
                hashObject->attributes.ticketSafe = SET;
        }
        // Output ticket
        out->validation.tag = TPM_ST_HASHCHECK;
        out->validation.hierarchy = in->hierarchy;

        if(in->hierarchy == TPM_RH_NULL)
        {
            // Ticket is not required
            out->validation.digest.t.size = 0;
        }
        else if(hashObject->attributes.ticketSafe == CLEAR)
        {
            // Ticket is not safe to generate
            out->validation.hierarchy = TPM_RH_NULL;
            out->validation.digest.t.size = 0;
        }
        else
        {
            // Compute ticket
            TicketComputeHashCheck(out->validation.hierarchy, hashAlg,
                                   &out->result, &out->validation);
        }
    }
    else
    {
        //   Update last piece of data
        CryptDigestUpdate2B(&hashObject->state.hmacState.hashState, &in->buffer.b);
#if !SMAC_IMPLEMENTED
        // Complete HMAC
        out->result.t.size = CryptHmacEnd(&(hashObject->state.hmacState),
                                          sizeof(out->result.t.buffer),
                                          out->result.t.buffer);
#else
        // Complete the MAC
        out->result.t.size = CryptMacEnd(&hashObject->state.hmacState, 
                                         sizeof(out->result.t.buffer),
                                         out->result.t.buffer);
#endif
        // No ticket is generated for HMAC sequence
        out->validation.tag = TPM_ST_HASHCHECK;
        out->validation.hierarchy = TPM_RH_NULL;
        out->validation.digest.t.size = 0;
    }
// Internal Data Update
    // mark sequence object as evict so it will be flushed on the way out
    hashObject->attributes.evict = SET;

    return TPM_RC_SUCCESS;
}

#endif // CC_SequenceComplete